package com.uduemc.security.core.filter;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter;

import com.uduemc.security.core.SecurityProperties;
import com.uduemc.security.core.component.ImageCode;
import com.uduemc.security.core.controller.ValidateController;
import com.uduemc.security.core.exception.ValidateCodeAuthenticationException;

@Component
// 当 miss name 为 "customValidateCodeFilter" 的 bean 时才会加载这个bean 
// 所以可以在工程中定义名字为 customValidateCodeFilter 的组件然后过滤器将会被换成自定义的
@ConditionalOnMissingBean(name = "customValidateCodeFilter")
// 继承 OncePerRequestFilter 标识过滤器执行一次
// 实现 InitializingBean 接口重写 afterPropertiesSet 方法是将bean注入完成后执行的方法
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 验证码校验失败处理器
	 */
	@Autowired
	private AuthenticationFailureHandler myAuthenticationFailureHandler;

	/**
	 * session 的工具处理类
	 */
	private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
	
	/**
	 * 在注入完bean后执行该方法
	 */
	@Override
	public void afterPropertiesSet() throws ServletException {
		super.afterPropertiesSet();
	}
	
	/**
	 * 在 WebSecurityConfigurerAdapter 配置中写入需要在登录提交表单的时执行此过滤器
	 */
	private String loginProcessingUrl;
	
	@Autowired
	private SecurityProperties securityProperties;
	
	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {
		// 获取配置中的需要过滤器执行的url
		List<String> accessUrl = securityProperties.getValidate().getAccessUrl();
		if(getLoginProcessingUrl() != null) {
			if(accessUrl == null) {
				accessUrl = new ArrayList<>();
			}
			accessUrl.add(getLoginProcessingUrl());
		}
		
		// 这里为了方便，统一将不判断请求的方式
		// !request.getMethod().equals("POST")
		
		/**
		 * 过滤当前的url判断是否需要验证码校验
		 */
		boolean isValidate = false;
		AntPathMatcher antPathMatcher = new AntPathMatcher();
		for(String aUrl : accessUrl) {
			if(antPathMatcher.match(aUrl, request.getRequestURI())) {
				isValidate = true;
				break;
			}
		}

		if (isValidate) {
			try {
				validate(new ServletWebRequest(request));
			} catch (ValidateCodeAuthenticationException e) {

				logger.info("myAuthenticationFailureHandler: " + myAuthenticationFailureHandler);
				myAuthenticationFailureHandler.onAuthenticationFailure(request, response, e);
				return;
			}
		}

		// 链接到下一个过滤器
		filterChain.doFilter(request, response);
	}

	private void validate(ServletWebRequest request)
			throws ServletRequestBindingException, ValidateCodeAuthenticationException {
		String stringParameter = ServletRequestUtils.getStringParameter(request.getRequest(), "validationCode");
		if (StringUtils.isBlank(stringParameter)) {
			throw new ValidateCodeAuthenticationException("验证码不能为空");
		}

		ImageCode imageCode = (ImageCode) sessionStrategy.getAttribute(request, ValidateController.SESSION_KEY);
		if (imageCode == null) {
			throw new ValidateCodeAuthenticationException("验证码不存在");
		}

		if (imageCode.isExpire()) {
			sessionStrategy.removeAttribute(request, ValidateController.SESSION_KEY);
			throw new ValidateCodeAuthenticationException("验证码已过期");
		}

		if (!StringUtils.equals(stringParameter, imageCode.getCode())) {
			throw new ValidateCodeAuthenticationException("验证码不正确");
		}

		sessionStrategy.removeAttribute(request, ValidateController.SESSION_KEY);
	}
	

	public String getLoginProcessingUrl() {
		return loginProcessingUrl;
	}

	public void setLoginProcessingUrl(String loginProcessingUrl) {
		this.loginProcessingUrl = loginProcessingUrl;
	}

}
